using System;
using System.Drawing;
using System.Collections.Generic;
using System.Drawing.Imaging;
using Clandestine.Base;
using Clandestine.Graphics;
using System.Diagnostics;

namespace Clandestine.Engines.Field
{
	public class Spritesheet
	{
		public string Filename { get; private set; }
		public int Number = 0; // debug
		
		public Map Map { get; private set; }
		
		private int gridSize;
		public int GridSize
		{
			get { return gridSize; }
			set
			{
				lock (loadedTiles)
					loadedTiles.Clear();
				gridSize = value;
			}
		}

		public int Width { get { return Texture.Size.Width; } }
		public int Height { get { return Texture.Size.Height; } }

		public int TileWidth  { get { return this.Width / this.GridSize; } }
		public int TileHeight { get { return this.Height / this.GridSize; } } 

		private Graphics.Graphics graphics;

		public readonly Texture Texture;

		private Dictionary<int, Texture> loadedTiles = new Dictionary<int, Texture>();

		public int ID { get { return this.Map.Spritesheets.IndexOf(this); } } 

		public Spritesheet(Map map, string filename, int gridSize, bool treatAsIndexed = false)
		{
			this.Map = map;
			this.graphics = this.Map.Graphics;
			this.Filename = filename;
			this.GridSize = gridSize;

			this.Texture = graphics.Textures.Get(filename) ?? graphics.Textures.AddBitmap(filename, new Bitmap(this.Filename), treatAsIndexed, false, true);

			// Check if it matches what we expect for GridSize
			if ((Width % this.GridSize != 0) || (Height % this.GridSize != 0))
				Log.w("Size of tilset '{0}' is not divisable by the grid size ({1}) it was loaded with; we're gonna roll with it though, bro'. If crazy shit goes down, you know why.", this.Filename, this.GridSize);
		}

		public void Dispose()
		{
			Texture.Dispose();

			lock (loadedTiles)
				foreach (Texture tex in loadedTiles.Values)
					tex.Dispose();
		}

		public string GetTileIdentifier(int n)
		{
			// Texture identifier format: filename.png!!gridSize!tileNum
			return string.Format("{0}!!{1}!{2}", this.Filename, this.GridSize, n);
		}

		public Texture GetTileTexture(int n)
		{
			lock (loadedTiles)
			{
				if (!loadedTiles.ContainsKey(n))
					CutSingleTile(n);

				return loadedTiles[n];
			}
		}

		// Cuts a single tile from the spritesheet and makes a texture for it!
		// Doesn't return anything as the tile can be gotten by some weird texture magic like "tileSheetName!x!y" or some such
		protected void CutSingleTile(int n)
		{
			int y = ((this.GridSize * n) / this.Width) * this.GridSize; // integer division!
			int x = (this.GridSize * n) % this.Width;

			Texture st = graphics.Textures.AddSub(Texture, new Rectangle(x, y, this.GridSize, this.GridSize), GetTileIdentifier(n), true);
			loadedTiles.Add(n, st);

			Debug.Assert(st.BottomRightTexCoord[0] <= 1f); // hope floating-point math doesn't trip us up here
			Debug.Assert(st.BottomRightTexCoord[1] <= 1f);
		}
	}
}

